package top.blackr.folivora.common.pay.pay;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import top.blackr.folivora.common.pay.constants.AliPayConstants;
import top.blackr.folivora.common.pay.exception.PayException;
import top.blackr.folivora.common.pay.function.PayConsumer;
import top.blackr.folivora.common.pay.pay.entity.*;
import top.blackr.folivora.common.pay.utils.WxRobotUtil;
import top.blackr.folivora.config.ApplicationProperties;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 支付宝支付默认实现，可直接继承该类
 *
 * @author BlackR
 */
@Slf4j
@Getter
public abstract class DefaultAliPayStrategy<PR extends PayCallbackLog> implements IPayStrategy<AliOrderStrResult, Map<String, String>, PR> {

    @Autowired
    private ApplicationProperties.AliPay aliProperties;
    @Autowired
    private AlipayConfig alipayConfig;

    @Override
    public AliOrderStrResult getOrderStr(OrderStrRequest request) {
        AliOrderStrResult orderStrResult = new AliOrderStrResult();
        try {
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
            model.setOutTradeNo(request.getOutTradeNo());
            // 设置最低支付金额
            if (request.getTotalAmount().compareTo(BigDecimal.ZERO) <= 0) {
                request.setTotalAmount(new BigDecimal("0.01"));
            }
            model.setTotalAmount(String.valueOf(request.getTotalAmount()));
            model.setSubject(request.getTitle());
            model.setSellerId(aliProperties.getSellerId());
            //设置异步通知地址
            AlipayTradeAppPayRequest tradeAppPayRequest = new AlipayTradeAppPayRequest();
            tradeAppPayRequest.setNotifyUrl(request.getNotifyUrl());
            tradeAppPayRequest.setBizModel(model);

            AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(tradeAppPayRequest);
            if (response.isSuccess()) {
                orderStrResult.setOrderStr(response.getBody());
            } else {
                orderStrResult.setErrorStr("网络异常!");
            }
        } catch (AlipayApiException exception) {
            orderStrResult.setErrorStr("获取OrderStr异常！" + exception.getErrMsg());
            return orderStrResult;
        }
        return orderStrResult;
    }

    @SneakyThrows
    @Override
    public PR noticeCallback(HttpServletRequest request, Integer transactionType, PayConsumer<String, Map<String, String>, PR> consumer) {
        Map<String, String[]> requestParams = request.getParameterMap();
        Map<String, String> params = new HashMap<>();
        for (String name : requestParams.keySet()) {
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }

        String outTradeNo = params.get("out_trade_no");
        PayCallbackLog payCallbackLog = getPayCallbackLog();
        payCallbackLog.setRetryNumber(0);
        payCallbackLog.setTradeNo(outTradeNo);
        payCallbackLog.setCreateTime(LocalDateTime.now());
        payCallbackLog.setTransactionType(transactionType);
        payCallbackLog.setCallbackStr(params.toString());
        payCallbackLog.setPayType(0);
        try {
            // 验签
            boolean signVerified = AlipaySignature.rsaCertCheckV1(params, aliProperties.getLocalFileUrl() + AliPayConstants.ALIPAY_CERT_PATH, "UTF-8", "RSA2");
            if (signVerified) {
                String appId = params.get("app_id");
                String sellerId = params.get("seller_id");
                if (!appId.equals(aliProperties.getAppId())) {
                    return resultLog(payCallbackLog, "fail", "APPID验证错误", false);
                }
                if (!sellerId.equals(aliProperties.getSellerId())) {
                    return resultLog(payCallbackLog, "fail", "订单创建人验证异常", false);
                }

                String tradeStatus = params.get("trade_status");
                payCallbackLog.setTradeStatus(tradeStatus);
                if (tradeStatus.equals(AliPayConstants.TRADE_SUCCESS)) {
                    payCallbackLog = consumer.accept(outTradeNo, params, payCallbackLog);
                }
                return resultLog(payCallbackLog, "success", "", true);
            } else {
                //  验签失败则记录异常日志，并在response中返回fail.
                return resultLog(payCallbackLog, "fail", "验签失败", false);
            }
        } catch (Exception e) {
            log.error("支付宝支付回调失败:", e);
            WxRobotUtil.sendException("aliPay", e);
            throw e;
        }
    }

    @SuppressWarnings("unchecked")
    public PR resultLog(PayCallbackLog payCallbackLog, Object result, String failReason, boolean noticeStatus) {
        payCallbackLog.setResult(result);
        payCallbackLog.setFailReason(failReason);
        payCallbackLog.setNoticeStatus(noticeStatus ? 0 : 1);
        return (PR) payCallbackLog;
    }

    @SneakyThrows
    @Override
    public <ORR extends OrderRefundRequest> void refund(ORR refundRequest) {
        AliOrderRefundRequest aliRefundRequest = (AliOrderRefundRequest) refundRequest;
        try {
            AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.set("out_trade_no", aliRefundRequest.getTradeNo());
            bizContent.set("refund_amount", aliRefundRequest.getRefundAmount());
            bizContent.set("out_request_no", aliRefundRequest.getRefundTradeNo());
            request.setBizContent(bizContent.toString());
            AlipayTradeRefundResponse response = alipayClient.certificateExecute(request);
            if (!response.isSuccess() && !"Y".equals(response.getFundChange())) {
                log.error("支付宝退款失败，body：【{}】\nmsg：【{}】", response.getBody(), response.getMsg());
                throw new PayException("支付宝退款失败");
            }
        } catch (PayException e) {
            log.error("支付宝退款失败，退款信息:{}", JSONUtil.toJsonStr(aliRefundRequest));
        } catch (Exception e) {
            log.error("支付宝退款失败，退款信息:{}", JSONUtil.toJsonStr(aliRefundRequest), e);
            throw e;
        }
    }

    public abstract PR getPayCallbackLog();

}
